import {
  CacheType,
  ButtonInteraction,
  ButtonBuilder,
  ButtonStyle,
} from 'discord.js';
import EventEmitter from '@3xpo/events';
import type Bot from './bot';
import bot from '../bot';
import { logger } from '../util/logger';
import { Embed } from './embed';
import { UserPermissions as UserPermission } from '../util/db/models/user';
import { buttonRegistry } from '../registerables/buttonRegistry';

type CommandEvents = {
  interact: (
    interaction: ButtonInteraction<CacheType>,
    bot: Bot,
    btn: Button,
  ) => any | Promise<any>;
};
export default class Button extends EventEmitter<CommandEvents> {
  public constructor(public id: string) {
    super();
  }
  public on<K extends keyof CommandEvents>(
    event: K,
    listener: CommandEvents[K],
  ): this {
    return super.on(
      event,
      event === 'interact'
        ? async (...a) => {
            try {
              return await listener(...a);
            } catch (error) {
              logger.error(
                `An error occurred while running button ${this.id}.`,
              );
              logger.error(error);
              const [interaction] = a;
              try {
                const embed = Embed.create().error(error);
                if (!interaction.replied)
                  interaction.reply({
                    embeds: [embed],
                  });
                else
                  interaction.followUp({
                    embeds: [embed],
                  });
              } catch (error) {
                logger.warn(
                  error,
                  'Failed to reply to user after encountering error',
                );
              }
            }
          }
        : listener,
    );
  }
  public setId(id: string) {
    if (this.label === this.id) this.label = id;
    if (this.registered) {
      buttonRegistry.delete(this.id);
      buttonRegistry.set(id, this);
    }
    this.id = id;
    return this;
  }
  public label = this.id;
  public setDefaultLabel(label: string) {
    this.label = label;
    return this;
  }
  public style = ButtonStyle.Primary;
  public setStyle(style: ButtonStyle) {
    this.style = style;
    return this;
  }
  public requiredBotPermissions: UserPermission[] = [];
  public requireBotPermission(permission: UserPermission) {
    this.requiredBotPermissions.push(permission);
    return this;
  }
  private registered = false;
  public register() {
    buttonRegistry.set(this.id, this);
    this.registered = true;
    return this;
  }
  public create() {
    return new ButtonBuilder()
      .setCustomId(this.id)
      .setLabel(this.label)
      .setStyle(this.style);
  }
  public invoke(interaction: ButtonInteraction<CacheType>) {
    return this.emit('interact', interaction, bot, this);
  }
}
